Skip to content

Mock actual API#2

Merged
terwey merged 3 commits intomainfrom
claude/fix-mock-server-structure-01LESuTKFc3C38uxufLr7SEH
Nov 15, 2025
Merged

Mock actual API#2
terwey merged 3 commits intomainfrom
claude/fix-mock-server-structure-01LESuTKFc3C38uxufLr7SEH

Conversation

@terwey
Copy link
Copy Markdown
Contributor

@terwey terwey commented Nov 15, 2025

The mock server was using custom simplified types that didn't match the actual 3commas API structure. This caused consumers to receive different data structures than the real API provides.

Critical Changes:

bot_events structure:

  • Before: 11 fields (action, coin, type, status, price, size, order_type, order_size, order_position, is_market, created_at)
  • After: 2 fields (message, created_at) - matching the real API

Bot and Deal types:

  • Now use full tcmock.Bot and tcmock.Deal generated from OpenAPI spec
  • Bot: ~50 fields matching real API instead of 4 custom fields
  • Deal: ~100 fields matching real API instead of 8 custom fields

claude and others added 3 commits November 15, 2025 17:35
BREAKING CHANGE: Mock server now uses generated OpenAPI types instead of simplified custom types

The mock server was using custom simplified types that didn't match the actual 3commas API structure. This caused consumers to receive different data structures than the real API provides.

## Critical Changes:

**bot_events structure:**
- Before: 11 fields (action, coin, type, status, price, size, order_type, order_size, order_position, is_market, created_at)
- After: 2 fields (message, created_at) - matching the real API

**Bot and Deal types:**
- Now use full tcmock.Bot and tcmock.Deal generated from OpenAPI spec
- Bot: ~50 fields matching real API instead of 4 custom fields
- Deal: ~100 fields matching real API instead of 8 custom fields

## Migration Guide:

### Before:
```go
mockServer.AddBot(server.Bot{
    ID: 1, Name: "Test", Enabled: true, AccountID: 123,
})
mockServer.AddDeal(1, server.Deal{
    ID: 101, BotID: 1, Status: "active",
    Events: []server.BotEvent{
        {Action: "place", Coin: "BTC", ...},
    },
})
```

### After:
```go
mockServer.AddBot(server.NewBot(1, "Test", 123, true))
deal := server.NewDeal(101, 1, "USDT_BTC", "active")
server.AddBotEvent(&deal, "Placing base order. Price: 50000.0")
mockServer.AddDeal(deal)
```

## New Helper Functions:
- `server.NewBot()` - Create bot with sensible defaults
- `server.NewDeal()` - Create deal with required fields
- `server.AddBotEvent()` - Add event with message and timestamp
- `TestServer.AddBotEventToDeal()` - Add event to existing deal

Fixes the core issue where the mock didn't behave like the actual 3commas server.
Add ability to load go-vcr cassettes to populate mock server with real 3commas API responses. This enables testing against actual production data without manual mocking.

## Features

**Core API:**
- `LoadVCRCassette(path)` - Load single VCR cassette
- `LoadVCRCassettes(...paths)` - Load multiple cassettes

**What Gets Loaded:**
- ✅ Bots with ALL 50+ fields from real API
- ✅ Deals with ALL 100+ fields from real API
- ✅ **bot_events PRESERVED exactly as recorded** (critical for testing)
- ✅ Auto-creates minimal bots when deals reference non-existent bot IDs

**Behavior:**
- Only processes successful (2xx) responses
- Errors on duplicate IDs (ensures clean VCR preparation)
- Skips non-GET requests
- Pattern matches: /ver1/deals/{id}/show, /ver1/deals, /ver1/bots

## Usage Example

```go
func TestWithRealData(t *testing.T) {
    mockServer := server.NewTestServer(t)
    defer mockServer.Close()

    // Load real API response from VCR cassette
    mockServer.LoadVCRCassette("testdata/fixtures/my_deal")

    // Deal now available with ALL real data including bot_events
    resp, _ := http.Get(mockServer.URL() + "/ver1/deals/123/show")
    var deal tcmock.Deal
    json.NewDecoder(resp.Body).Decode(&deal)

    // bot_events preserved from real 3commas API!
    assert.Len(t, deal.BotEvents, 3)
}
```

## Why This Matters

VCR cassettes contain REAL complex data from 3commas that would be tedious to mock manually:
- Complex bot_events messages with actual trading activity
- All 100+ deal fields with real profit/loss calculations
- Actual account names, pairs, and trading strategies

This makes testing more realistic and maintainable.

## Dependencies

- Added gopkg.in/dnaeon/go-vcr.v3 for YAML parsing

## Tests

- TestLoadVCRCassette_SingleDeal - Basic loading and verification
- TestLoadVCRCassette_DuplicateError - Duplicate ID detection
- TestLoadVCRCassettes_Multiple - Loading multiple cassettes
- TestExampleVCRLoading - Example usage pattern
@terwey terwey marked this pull request as ready for review November 15, 2025 18:48
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/vcr.go
Comment on lines +26 to +30
func (ts *TestServer) LoadVCRCassette(cassettePath string) error {
// Load cassette from file
c, err := cassette.Load(cassettePath)
if err != nil {
return fmt.Errorf("failed to load VCR cassette %s: %w", cassettePath, err)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Ensure VCR cassette path includes .yaml extension

The new LoadVCRCassette forwards the caller’s path directly to cassette.Load, but the go‑vcr loader expects the actual filename including its .yaml suffix. Every new test and example invokes this helper with a basename (e.g. ../testdata/fixtures/deal_2376446537), assuming the library appends the extension, so cassette.Load will fail with “no such file or directory” and no data will be loaded. Either append .yaml in LoadVCRCassette before calling cassette.Load or update callers to pass the complete filename.

Useful? React with 👍 / 👎.

@terwey terwey merged commit 775428e into main Nov 15, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants